//#include "mesh_procedural.h"
//#include "vertex.h"
//#include "vector2.h"
//#include <vector>
//
//using namespace krryn::scene;
//using namespace krryn::math;
//
//namespace{
//	uv sphere_uv(const vector3f &a_Position){
//		uv l_UV;
//		l_UV.m_U = a_Position.m_X / (a_Position.length());
//		l_UV.m_V = a_Position.m_Y / (a_Position.length());
//		return l_UV;
//	}
//
//	vertex midpoint(const vertex &a_V1, const vertex &a_V2){
//		return vertex(((a_V1.m_Position + a_V2.m_Position) / 2.f).normalize());
//	}
//
//	void push_triangle(std::vector<size_t> &a_Vector, size_t a_V1, size_t a_V2, size_t a_V3){
//		a_Vector.push_back(a_V1);
//		a_Vector.push_back(a_V2);
//		a_Vector.push_back(a_V3);
//	}
//
//	struct triangle_edge{
//		size_t m_Start, m_Center, m_End;
//	};
//
//	size_t process(
//		std::vector<triangle_edge> &a_Edges, 
//		std::vector<size_t> &a_Indices, 
//		std::vector<vertex> &a_Vertices,
//		size_t a_Start,
//		size_t a_End)
//	{
//		// see if we've already processed this edge and return it if we have
//		for(std::vector<triangle_edge>::iterator i = a_Edges.begin(); i != a_Edges.end(); i++){
//			if(i->m_Start == a_Start && i->m_End == a_End || i->m_Start == a_End && i->m_End == a_Start){
//				return i->m_Center;
//			}
//		}
//
//		// find the middle of the edge (on a sphere)
//		vertex l_NewVertex = midpoint(a_Vertices[a_Indices[a_Start]], a_Vertices[a_Indices[a_End]]);
//
//		// store the new vertex
//		size_t l_Position = a_Vertices.size();
//		a_Vertices.push_back(l_NewVertex);
//
//		// store the edge so we don't process it twice
//		triangle_edge l_Edge;
//		l_Edge.m_Start  = a_Start;
//		l_Edge.m_Center = l_Position;
//		l_Edge.m_End    = a_End;
//
//		a_Edges.push_back(l_Edge);
//
//		// return the index
//		return l_Position;
//	}
//}
//
//mesh *mesh_procedural::create_sphere(const vector3f &a_Size, int a_Level) const{
//	std::vector<size_t> m_IndexBuffer;
//	std::vector<vertex> m_VertexBuffer;
//	std::vector<triangle_edge> m_EdgeList;
//
//	m_VertexBuffer.push_back(vertex(vector3f(1,  0,  0)));
//	m_VertexBuffer.push_back(vertex(vector3f(-1, 0,  0)));
//	m_VertexBuffer.push_back(vertex(vector3f(0,  1,  0)));
//	m_VertexBuffer.push_back(vertex(vector3f(0, -1,  0)));
//	m_VertexBuffer.push_back(vertex(vector3f(0,  0,  1)));
//	m_VertexBuffer.push_back(vertex(vector3f(0,  0, -1)));
//
//	m_VertexBuffer[0].m_Color.m_G = 255;
//	m_VertexBuffer[1].m_Color.m_B = 255;
//	m_VertexBuffer[2].m_Color.m_R = 255;
//
//	m_VertexBuffer[3].m_Color.m_G = 255;
//	m_VertexBuffer[4].m_Color.m_B = 255;
//	m_VertexBuffer[5].m_Color.m_R = 255;
//
//	push_triangle(m_IndexBuffer, 4, 0, 2);
//	push_triangle(m_IndexBuffer, 5, 2, 0);
//	push_triangle(m_IndexBuffer, 4, 2, 1);
//	push_triangle(m_IndexBuffer, 1, 5, 2);
//	push_triangle(m_IndexBuffer, 4, 1, 3);
//	push_triangle(m_IndexBuffer, 5, 3, 1);
//	push_triangle(m_IndexBuffer, 4, 3, 0);
//	push_triangle(m_IndexBuffer, 5, 0, 3);
//
//	for(int l = 1; l < a_Level; l++){
//		size_t l_OldSize = m_IndexBuffer.size();
//
//		std::vector<size_t> m_New;
//
//		for(size_t i = 0; i < l_OldSize; i += 3){
//			//vertex   l_A, l_B, l_C;
//			//vector3f l_Normal;
//			//size_t   l_iA, l_iB, l_iC; // indices in the vertex buffer
//
//			//l_A = midpoint(m_VertexBuffer[m_IndexBuffer[i + 0]], m_VertexBuffer[m_IndexBuffer[i + 2]]);
//			//l_B = midpoint(m_VertexBuffer[m_IndexBuffer[i + 0]], m_VertexBuffer[m_IndexBuffer[i + 1]]);
//			//l_C = midpoint(m_VertexBuffer[m_IndexBuffer[i + 1]], m_VertexBuffer[m_IndexBuffer[i + 2]]);
//
//			//l_A.m_Color.m_R = 255;
//			//l_B.m_Color.m_G = 255;
//			//l_C.m_Color.m_B = 255;
//
//			//l_iA = m_VertexBuffer.size();
//			//m_VertexBuffer.push_back(l_A);
//
//			//l_iB = m_VertexBuffer.size();
//			//m_VertexBuffer.push_back(l_B);
//
//			//l_iC = m_VertexBuffer.size();
//			//m_VertexBuffer.push_back(l_C);
//
//			size_t l_iA = process(m_EdgeList, m_IndexBuffer, m_VertexBuffer, i + 0, i + 2);
//			size_t l_iB = process(m_EdgeList, m_IndexBuffer, m_VertexBuffer, i + 0, i + 1);
//			size_t l_iC = process(m_EdgeList, m_IndexBuffer, m_VertexBuffer, i + 1, i + 2);
//
//			m_VertexBuffer[l_iA].m_Color.m_R = 255;
//			m_VertexBuffer[l_iB].m_Color.m_G = 255;
//			m_VertexBuffer[l_iC].m_Color.m_B = 255;
//
//			push_triangle(m_New, m_IndexBuffer[i + 0], l_iB, l_iA);
//			push_triangle(m_New, l_iB, m_IndexBuffer[i + 1], l_iC);
//			push_triangle(m_New, l_iB, l_iA, l_iC);
//			push_triangle(m_New, l_iA, l_iC, m_IndexBuffer[i + 2]);
//		}
//
//		m_IndexBuffer.swap(m_New);
//		//m_EdgeList.swap(std::vector<triangle_edge>());
//	}
//
//	// Scale the sphere
//	for(std::vector<vertex>::iterator i = m_VertexBuffer.begin(); i != m_VertexBuffer.end(); i++){
//		i->m_Position *= a_Size;
//		uv l_UV = sphere_uv(i->m_Position);
//		i->m_U = l_UV.m_U;
//		i->m_V = l_UV.m_V;
//	}
//
//	// Create the mesh
//	size_t *l_Indices = new size_t[m_IndexBuffer.size()];
//	vertex *l_Vertices = new vertex[m_VertexBuffer.size()];
//
//	std::copy(m_IndexBuffer.begin(), m_IndexBuffer.end(), l_Indices);
//	std::copy(m_VertexBuffer.begin(), m_VertexBuffer.end(), l_Vertices);
//
//	mesh *l_Mesh = new mesh();
//	l_Mesh->set_vertex_count(m_VertexBuffer.size());
//	l_Mesh->set_index_count(m_IndexBuffer.size());
//	l_Mesh->set_vertices(l_Vertices);
//	l_Mesh->set_indices(l_Indices);
//
//	l_Mesh->update_normals();
//
//	return l_Mesh;
//}

#include "mesh_procedural.h"
#include "constants.h"
#include "algebra.h"
#include "mesh.h"
#include "vector3.h"

using namespace krryn::scene;
using namespace krryn::math;

mesh *mesh_procedural::create_sphere(const vector3f &a_Size, int a_Level) const{
	if(a_Level < 1)
		return 0;

	int a_Slices		= a_Level + 2;
	int a_Segments		= a_Level + 3;
	int l_VertexCount	= a_Slices * a_Segments;
	int l_TriangleCount	= 2 * (a_Slices - 1) * (a_Segments - 1);
	int l_IndexCount	= l_TriangleCount * 3;

	vertex *l_Vertices	= new vertex[l_VertexCount];
	size_t *l_Indices	= new size_t[l_IndexCount];
	mesh *l_Mesh		= new mesh();

	l_Mesh->set_index_count(l_IndexCount);
	l_Mesh->set_vertex_count(l_VertexCount);
	l_Mesh->set_vertices(l_Vertices);
	l_Mesh->set_indices(l_Indices);

	// Generate index buffer
	for(int i = 0; i < (a_Slices - 1); i++){
		for(int j = 0; j < (a_Segments - 1); j++){
			int l_Base			= i * a_Segments;
			int l_SegmentIndex	= l_Base + j;
			int l_BaseIndex		= (i * a_Slices + j) * 6;

			l_Indices[l_BaseIndex + 0] = l_SegmentIndex;
			l_Indices[l_BaseIndex + 1] = l_Base + j + 1;
			l_Indices[l_BaseIndex + 2] = l_SegmentIndex + a_Segments;

			l_Indices[l_BaseIndex + 3] = l_Base + j + 1;
			l_Indices[l_BaseIndex + 4] = l_SegmentIndex + a_Segments + 1;
			l_Indices[l_BaseIndex + 5] = l_SegmentIndex + a_Segments;
		}
	}

	// generate vertex buffer
	for(int i = 0; i < a_Slices; i++){
		float l_HeightAngle = ((float)i / (a_Slices - 1)) * math::PI;
		float l_Radius		= (float)math::abs(.5f * math::sin(l_HeightAngle));
		float l_Y			= .5f * math::cos(l_HeightAngle);

		for(int j = 0; j < a_Segments; j++){
			float l_Angle	= ((float)j / a_Slices) * 2.f * math::PI;
			float l_X		= l_Radius * math::sin(l_Angle);
			float l_Z		= l_Radius * math::cos(l_Angle);
			int l_Point		= i * a_Segments + j;

			vector3f l_Position(l_X, l_Y, l_Z);

			// normalize to create a unit sphere
			l_Position = l_Position.normalize();

			// simple spherical projection to generate UV coordinates
			l_Vertices[l_Point].m_U = l_Position.m_X / (l_Position.length());
			l_Vertices[l_Point].m_V = l_Position.m_Y / (l_Position.length());

			// scale after UVing
			l_Position *= a_Size;

			l_Vertices[l_Point].m_Position = l_Position;

			// white sphere
			l_Vertices[l_Point].m_Color = color3::identity();
		}
	}

	l_Mesh->update_normals();
	l_Mesh->update_aabb();

	return l_Mesh;
}

mesh *mesh_procedural::create_quad(const vector3f &a_Size) const{
	int l_VertexCount	= 4;
	int l_IndexCount	= 6;

	vertex *l_Vertices	= new vertex[l_VertexCount];
	size_t *l_Indices	= new size_t[l_IndexCount];
	mesh *l_Mesh		= new mesh();

	l_Mesh->set_index_count(l_IndexCount);
	l_Mesh->set_vertex_count(l_VertexCount);
	l_Mesh->set_vertices(l_Vertices);
	l_Mesh->set_indices(l_Indices);

	// Camera facing 'unit' quad
	l_Vertices[0] = vertex(vector3f(-1, +1, 2));
	l_Vertices[1] = vertex(vector3f(+1, +1, 2));
	l_Vertices[2] = vertex(vector3f(-1, -1, 2));
	l_Vertices[3] = vertex(vector3f(+1, -1, 2));

	for(int i = 0; i < l_VertexCount; i++){
		l_Vertices[i].m_U = l_Vertices[i].m_Position.m_X;
		l_Vertices[i].m_V = l_Vertices[i].m_Position.m_Y;

		l_Vertices[i].m_Position *= a_Size / 2.f;
	}

	l_Indices[0] = 0;
	l_Indices[1] = 1;
	l_Indices[2] = 2;

	l_Indices[3] = 1;
	l_Indices[4] = 2;
	l_Indices[5] = 3;

	l_Mesh->update_normals();
	l_Mesh->update_aabb();

	return l_Mesh;
}